#include "StdAfx.h"
#include "IOCP.h"
#include <windows.h>
#include "DebugOut.h"
#include "AcceptSocket.h"

#pragma comment(lib, "Ws2_32")


CIOCP::CIOCP(void)
{
	m_CompletionPort = NULL;
	InitWSA();
	m_bStop = TRUE;
}


CIOCP::~CIOCP(void)
{
	if(m_CompletionPort != NULL)
	{
		CloseHandle(m_CompletionPort);
	}

	m_bStop = TRUE;

	for(int i = 0; i < m_vThreadHs.size(); i++)
	{
		WaitForSingleObject(m_vThreadHs[i], INFINITE);
	}
}

int CIOCP::InitWSA()
{
	static WSADATA wsaData;
	int nRet = WSAStartup(0x0202, &wsaData);
	return nRet;
}

void CIOCP::StartListen(unsigned short sPort)
{
	m_CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
	 
	if(m_CompletionPort == NULL)
	{
		dbg_printf("CreateIoCompletionPort error: %d\n", GetLastError());
		return;
	}

	m_sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,WSA_FLAG_OVERLAPPED); 
	
	m_SAddr.sin_family = AF_INET;
	m_SAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	m_SAddr.sin_port = htons(sPort);
	if(bind(m_sListen, (PSOCKADDR)&m_SAddr, sizeof(m_SAddr)) != 0)
	{
		dbg_printf("bind error: %d\n", GetLastError());
		return;
	}

	if(listen(m_sListen, 5) != 0)
	{
		dbg_printf("listen error: %d\n", GetLastError());
		return;
	}


	CreateMultiThread();
}

CAcceptSocket* CIOCP::AcceptSocket()
{
	SOCKET hClient; 
	if((hClient = WSAAccept(m_sListen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
	{
		dbg_printf("WSAAccept error %d\n", WSAGetLastError());
		return NULL;
	}

	CAcceptSocket* p = new CAcceptSocket(hClient);

	CreateIoCompletionPort((HANDLE)hClient, m_CompletionPort, 
		                        (DWORD)p, 0);
	
	p->head_accept();
	return p;
}

DWORD WINAPI CIOCP::ServerWorkerThread(LPVOID param)
{
	CIOCP* pIOCP = (CIOCP*)param;
	pIOCP->IOThread();
	return 1;
}

void CIOCP::CreateMultiThread()
{
	SYSTEM_INFO		SystemInfo;
	int				nCount = 0;

	GetSystemInfo(&SystemInfo);
	nCount = SystemInfo.dwNumberOfProcessors * 2;

	m_vThreadIDs.resize(nCount);
	m_vThreadHs.resize(nCount);

	m_bStop = FALSE;
	for(int i = 0; i < nCount; i++)
	{
		HANDLE ThreadHandle;

		if((m_vThreadHs[i] = CreateThread(NULL, 0, ServerWorkerThread, this,
			0, &m_vThreadIDs[i])) == NULL)
		{
			dbg_printf("CreateThread failed with error %d\n", GetLastError());
			return;
		}      
	}
}

void CIOCP::IOThread()
{
	OVERLAPPEDEX*	pOlEx;
	DWORD			offSetNumber = 0;
	DWORD			key;
	int				nRet = 0;

	while(!m_bStop)
	{
		nRet = GetQueuedCompletionStatus(m_CompletionPort, 
			&offSetNumber, 
			&key, 
			(LPOVERLAPPED*)&pOlEx, 
			INFINITE);

		if(0 == nRet)
		{
			dbg_printf("GetQueuedCompletionStatus error %d\n", GetLastError());
			continue;
		}

		if(0 == offSetNumber)
		{
			if(pOlEx != NULL)
			{
				pOlEx->pAcceptSocket->handle_excpet(pOlEx->pMsg, offSetNumber);
			}
			continue;
		}

		pOlEx->pAcceptSocket->handle_msg(pOlEx->pMsg, offSetNumber);
	}
}
